use std::marker::PhantomData;

use bytes::Bytes;
use hashbrown::HashMap;
use honeybee::prelude::*;
use honeybee_proto::{client, hall, room};
use once_cell::sync::Lazy;

use crate::user::User;

type Process<A> = fn(&Addr<A>, Bytes) -> honeybee::Result<()>;

pub struct Register<A>(HashMap<u16, Process<A>>, PhantomData<A>);

impl<A> Register<A> {
    fn new() -> Self {
        Self(HashMap::default(), PhantomData)
    }

    pub fn register<T: 'static + prost::Message + MessageId + Default>(&mut self)
    where
        A: AnyHandler<T>,
    {
        let r = self.0.insert(T::ID, |user_addr, bytes| {
            let message = T::decode(bytes)?;
            user_addr.any_send(message)
        });
        assert!(r.is_none(), "Duplicated MessageId {}", T::ID);
    }

    pub fn process(&self, user_addr: &Addr<A>, msg: UserRawMsg) -> honeybee::Result<()> {
        let process = self
            .0
            .get(&msg.id)
            .ok_or_else(|| anyhow::anyhow!("msssage id:{} not registry", msg.id))?;
        process(user_addr, msg.bytes)
    }
}

pub fn message_process(user_addr: &Addr<User>, msg: UserRawMsg) -> honeybee::Result<()> {
    REGISTER.process(user_addr, msg)
}

static REGISTER: Lazy<Register<User>> = Lazy::new(|| {
    let mut register = Register::new();
    register.register::<hall::HeartBeat>();
    register.register::<hall::Person>();
    register.register::<room::RspCreateRoomState>();
    register.register::<client::RspKickout>();
    register.register::<client::RspLocalUscAppTest>();
    register
});
